package br.com.massai.view.utils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import java.util.regex.Pattern;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import br.com.massai.model.exceptions.ErrorCore;
import br.com.massai.model.exceptions.SystemException;

@SuppressWarnings({ "all" })
public class NumberUtil {
	
	private static final Logger logger = LogManager.getLogger(NumberUtil.class);

	/**
	 * Retorna o Locale pt BR.
	 * 
	 * @return o Locale pt BR.
	 */
	private static Locale getLocaleBrasil() {
		return new Locale("pt", "BR");
	}

	/**
	 * Converte uma String no formato pt BR para um double. Ex: 10.000,05 sera
	 * retornado o valor float 10000.05
	 * 
	 * @param valor
	 *            uma String contendo o valor a ser convertido
	 * @return Um valor float que representa a String informada.
	 * @throws SystemException
	 *             Caso não seja possível fazer a conversão.
	 */
	public static float converterFloat(String valor) throws SystemException {

		Number number = converter(valor);
		return number.floatValue();
	}

	/**
	 * Converte uma String no formato pt BR para um double. Ex: 10.000,05 sera
	 * retornado o valor double 10000.05
	 * 
	 * @param valor
	 *            uma String contendo o valor a ser convertido
	 * @return Um valor double que representa a String informada.
	 * @throws SystemException
	 *             Caso não seja possível fazer a conversão.
	 */
	public static double converterDouble(String valor) throws SystemException {

		Number number = converter(valor);
		return number.doubleValue();
	}

	/**
	 * 
	 * método responsável pela conversão de um valor para um tipo Double.
	 * 
	 * @param numero
	 *            o número
	 * @param numeroCasasDecimais
	 *            a quantidade de casas decimais
	 * @return o valor convertido para Double
	 */
	public static Double converterDouble(Number numero,
			Integer numeroCasasDecimais) {
		return converterBigDecimal(numero, numeroCasasDecimais).doubleValue();
	}

	/**
	 * 
	 * método responsável pela conversão de um valor para um tipo BigDecimal.
	 * 
	 * @param numero
	 *            o número
	 * @param numeroCasasDecimais
	 *            a quantidade de casas decimais
	 * @return o valor convertido para BigDecimal
	 */
	public static BigDecimal converterBigDecimal(Number numero,
			Integer numeroCasasDecimais) {
		return new BigDecimal(new BigInteger(numero.toString()),
				numeroCasasDecimais);
	}

	/**
	 * Converte uma String no formato pt BR para um Number.
	 * 
	 * @param valor
	 *            Uma String no formato 10.000,05
	 * @return Um number com o valor informado.
	 * @throws SystemException
	 *             Caso não seja possível fazer a conversão.
	 */
	public static Number converter(String valor) throws SystemException {

		Number number = null;

		try {
			Locale locale = getLocaleBrasil();
			DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
			NumberFormat nf = new DecimalFormat("#,##0.00", dfs);
			number = nf.parse(valor);

		} catch (ParseException e) {
			logger.error("não foi possível converter " + valor, e);
			throw new SystemException(ErrorCore.ERROR_SYSTEM, e);
		}

		return number;
	}

	/**
	 * Formata um double informado em uma String no formato do Locale pt BR e
	 * com duas casas decimais.
	 * 
	 * @param valor
	 *            o valor a ser formatado.
	 * @return A String formatada
	 */
	public static String formatar(double valor) {
		return formatar(valor, getLocaleBrasil());
	}

	/**
	 * Formata um BigDecimal informado em uma String no formato do Locale pt BR
	 * e com duas casas decimais.
	 * 
	 * @param valor
	 *            o valor a ser formatado.
	 * @return A String formatada
	 */
	public static String formatar(BigDecimal valor) {
		return formatar(valor.doubleValue());
	}

	/**
	 * Formata um double informado em uma String no formato do Locale informado
	 * e com duas casas decimais.
	 * 
	 * @param valor
	 *            o valor a ser formatado
	 * @param locale
	 *            um Locale contendo o locale
	 * @return A String formatada
	 */
	public static String formatar(double valor, Locale locale) {

		NumberFormat nf = NumberFormat.getNumberInstance(locale);
		nf.setMinimumFractionDigits(2);
		nf.setMaximumFractionDigits(2);

		return nf.format(valor);
	}

	/**
	 * Retorna se a String informada eh um número válido.
	 * 
	 * @param string
	 *            a string a ser validada
	 * @return <code>true</code> se a String informada for um número válido,
	 *         <code>false</code> caso contrário.
	 */
	public static boolean ehNumero(String string) {
		boolean ehNumero = true;

		try {
			converter(string);
		} catch (Exception e) {
			ehNumero = false;
		}

		return ehNumero;
	}

	/**
	 * Verifica se a String informada como argumento eh constituída apenas de
	 * números.
	 * 
	 * @param valor
	 *            número a ser validado.
	 * @return <code>true</code> se a String informada como argumento eh
	 *         constituída apenas de números e <code>false</code> caso
	 *         contrário.
	 */
	public static boolean ehSomenteNumero(String valor) {

		final String NUMERO_REGEX = "^[0-9]+$";
		return Pattern.compile(NUMERO_REGEX).matcher(valor).matches();
	}

	/**
	 * Obtem a soma dos digitos de um número.
	 * 
	 * @param numero
	 *            o número
	 * @return a soma dos digitos
	 */
	public static int obterSomaDigitos(int numero) {

		numero = Math.abs(numero);
		int somaDigitos = 0, divisor = 10;

		while (numero != 0) {
			int resto = numero % divisor;
			somaDigitos += resto;
			numero /= divisor;
		}

		return somaDigitos;
	}
	
	/**
	 * Remove máscara de CNPJ formatado.
	 * 
	 * @param cnpjComMascara
	 *            CNPJ com máscara
	 * @return CNPJ sem máscara
	 */
	public static String removerFormato(String numeroComMascara) {

		StringBuffer numero = new StringBuffer(numeroComMascara);
		char ch = ' ';

		for (int i = 0; i < numero.length(); i++) {
			ch = numero.charAt(i);
			if (ch == '.' || ch == ',') {
				numero.deleteCharAt(i);
			}
		}

		return numero.toString();
	}

}
